﻿using Slex;
using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Linq;
using ArithCollect;
using System.Reflection;

namespace CaseMap {
    public class Map {
        static Map _obj;
        public static Map Current { get { if (_obj == null)_obj = new Map(); return _obj; } }
        private MudelManager mm;
        public MudelManager MudelMangage { get { return mm; } }
        public Map() {
            mm = new MudelManager();
        }

        /// <summary>     
        ///Load(null, Map.Navh(LoadViewModul, "View.Home", "Background", null, null));
        ///        Map.Navh("View.Home", "Background", null, null),
        ///        Map.Navh("View.Home", "Background", null, null),
        ///        Map.Navh("View.Home", "Background", null, null)
        ///        );
        /// </summary>
        /// <param name="container"></param>
        /// <param name="nvps"></param>
        public void Initialize(UserControl container, Brushs background = Brushs.Snow) {
            Layout.Current.Container = container;
            Layout.Current.Background = background.ToBrush();
        }
        //
        public void ApplyLayout(FrameworkElement old, Orientation rc, bool increasecombine, params FrameworkElement[] views) {
            var p = old.Parent as Area;
            if (p != null) {//
                if (p.Children.Count == 1) {
                    p.ClearAll();
                    if (rc == Orientation.Vertical) {
                        p.AddRow(views);
                    } else {
                        p.AddColum(views);
                    }
                } else {
                    if (views.Length == 1 && !increasecombine) {
                        p.AddOne(views[0]);
                    } else {
                        var cn = Area.GetColumn(old);
                        var rn = Area.GetRow(old);
                        var index = p.Children.IndexOf(old);
                        var a = new Area();
                        Area.SetColumn(a, cn); Area.SetRow(a, rn);
                        if (rc == Orientation.Vertical) {
                            a.AddRow(views);
                        } else {
                            a.AddColum(views);
                        }
                        p.Children.Remove(old);
                        p.Children.Insert(index, a);
                    }
                }
            }
        }
        public void ApplyLayout(FrameworkElement old, FrameworkElement view, bool isuncombine = true) {
            var p = old.Parent as Area;
            if (p != null) {
                if (!isuncombine) {
                    p.AddOne(view);
                } else {
                    var cn = Area.GetColumn(old);
                    var rn = Area.GetRow(old);
                    var index = p.Children.IndexOf(old);
                    p.Children.Remove(old);
                    Area.SetColumn(view, cn); Area.SetRow(view, rn);
                    p.Children.Insert(index, view);
                }
            }
        }

        public void ApplyLayout(Orientation rc, params FrameworkElement[] views) {
            Layout.Current.layoutroot.ClearAll();
            if (rc == Orientation.Vertical) {
                Layout.Current.layoutroot.AddRow(views);
            } else {
                Layout.Current.layoutroot.AddColum(views);
            }
        }
        public void ApplyLayout(string xap, string page) {
            RemoteLoadDo(xap, page, paged => {
                Layout.Current.layoutroot.AddOne(paged);
            });
        }
        public void ApplyLayout(string xap, string page, FrameworkElement old, bool increaseOrCombine = true) {
            RemoteLoadDo(xap, page, paged => {
                var p = old.Parent as Area;
                if (p != null) {//
                    if (p.Children.Count == 1 || !increaseOrCombine) {
                        p.AddOne(paged);
                    } else {
                        var cn = Area.GetColumn(old);
                        var rn = Area.GetRow(old);
                        Area.SetColumn(paged, cn); Area.SetRow(paged, rn);
                        var index = p.Children.IndexOf(old);
                        p.Children.Remove(old);
                        p.Children.Insert(index, paged);
                    }
                }
            });
        }
        public void ApplyLayout(int deep, int index, Orientation rc, params FrameworkElement[] views) {
            var area = SearchBone(deep, index);
            area.ClearAll();
            if (rc == Orientation.Vertical) {
                area.AddRow(views);
            } else {
                area.AddColum(views);
            }
        }
        public void ApplyLayout(string xap, string page, int deep, int index) {
            RemoteLoadDo(xap, page, paged => {
                var area = SearchBone(deep, index);
                area.AddOne(paged);
            });
        }
        public void ApplyLayout(FrameworkElement fe) {
            Layout.Current.layoutroot.AddOne(fe);
        }
        public void LoadResource(string xap, string page) {
            RemoteLoadDo(xap, page, paged => {
                MergeResource(paged);
            });
        }
        internal void RemoteLoadDo(string xap, string page, Action<FrameworkElement> act) {
            try {
                var paged = mm.GetPage(xap, page) as FrameworkElement;
                act.Invoke(paged);
            } catch (BagException e) {
                e.Recoder(this, this.GetType().GetMethod("RemoteLoadDo", BindingFlags.NonPublic | BindingFlags.Instance), new object[] { xap, page, act });
            }
        }
        public void MergeResource(FrameworkElement fe) {
            //fe.Resources
            //Application.Current.Resources.MergedDictionaries.Clear();
            var rescount = fe.Resources.MergedDictionaries.Count;
            for (int i = 0; i < rescount; i++) {
                var res = fe.Resources.MergedDictionaries[i];
                if (!Application.Current.Resources.MergedDictionaries.Contains(res)) {
                    fe.Resources.MergedDictionaries.Remove(res);
                    rescount--;
                    Application.Current.Resources.MergedDictionaries.Add(res);
                }
            }
        }
        public Area BuildArea(Orientation rc, params FrameworkElement[] ucs) {
            var a = new Area();
            foreach (var item in ucs) {
                item.Independent();
            }
            if (rc == Orientation.Vertical) {
                a.AddRow(ucs);
            } else {
                a.AddColum(ucs);
            }
            return a;
        }
        public Area BuildArea(FrameworkElement uc) {
            var a = new Area();
            uc.Independent();
            a.AddOne(uc);
            return a;
        }
        public void ClearCache() {
            mm.pages.Clear();
        }
        /// <summary>
        /// 调用Ex.SendMessage方法时先设置 此id值，在跳转框架上获取之id值
        /// 参见 Slex 中的 SendMessage,ReceiveMessage 两个方法。
        /// http://msdn.microsoft.com/zh-cn/library/system.windows.messaging.localmessagesender(v=vs.95).aspx
        /// </summary>
        public string JumpId { get; set; }
        public event Action<object, string, string> JumpTo;
        public void Jump(UserControl root, string id, string msg, Brushs background = Brushs.Snow) {
            Initialize(root, background);
            if (JumpTo != null) {
                JumpTo.Invoke(this, id, msg);
            }
        }
        public T Search<T>(string xap, string page) where T : class {
            var t = default(T);
            var item = from it in mm.pages where it.Item2 == page && it.Item3 == xap select it;
            if (item != null && item.Count() > 0)
                t = item.First()[xap][page] as T;
            return t;
        }
        public T Search<T>(params int[] index) where T : class {
            var t = default(T);
            dynamic temp = Layout.Current.layoutroot;
            foreach (var item in index) {
                if (temp != null) {
                    if (temp is Area) {
                        temp = temp.Children[item];
                    }
                }
            }
            t = temp as T;
            return t;
        }
        public T Search<T>(Func<T, bool> fun) where T : class {
            foreach (var item in GetChildren(Layout.Current.layoutroot)) {
                var t = item as T;
                if (fun.Invoke(t)) {
                    return t;
                }
            }
            return null;
        }
        IEnumerable<UIElement> GetChildren(Area a) {
            foreach (var item in a.Children) {
                if (item is Area) {
                    foreach (var item2 in GetChildren(item as Area)) yield return item2;
                } else {
                    yield return item;
                }
            }
        }
        Area SearchBone(int deep, int index) {
            var x = BreadthSearch(new HashSet<Area>(new[] { Layout.Current.layoutroot }), 0);
            //var x2 = x.ToArray();
            var x3 = from item in x where item.Item1 == deep select item;
            var x4 = x3.First().Item2;
            return x4.ElementAt(index);
        }
        HashSet<Area> GatherArea(Area a) {
            var als = new HashSet<Area>();
            foreach (var item in a.Children) {
                if (item is Area) {
                    als.Add(item as Area);
                }
            }
            return als;
        }
        IEnumerable<Tople<int, HashSet<Area>>> BreadthSearch(HashSet<Area> hsas, int i) {
            var nextareas = new HashSet<Area>();
            foreach (var item in hsas) {
                nextareas = nextareas.Union(GatherArea(item));
            }
            if (nextareas.Count > 0) {
                yield return new Tople<int, HashSet<Area>>(++i, nextareas);
                foreach (var item in BreadthSearch(nextareas, i)) yield return item;
            }
        }
    }
}
